package cap

import (
	"crypto/rand"
	"errors"
	"fmt"
	"math/big"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// getCurrentFilePath 获取当前源文件的路径
func getCurrentFilePath() string {
	_, filename, _, _ := runtime.Caller(1)
	return filename
}

// GetDefaultFontDir 获取默认的字体目录路径
func GetDefaultFontDir() string {
	// 获取当前工作目录
	currentDir, err := os.Getwd()
	if err != nil {
		currentDir = "."
	}
	
	// 首先尝试使用工作目录下的相对路径
	fontDir := filepath.Join(currentDir, "fonts", "Roboto")
	if _, err := os.Stat(fontDir); err == nil {
		return fontDir
	}
	
	// 如果工作目录下找不到，尝试使用当前源文件所在目录
	currentFilePath := getCurrentFilePath()
	sourceDir := filepath.Dir(currentFilePath)
	fontDir = filepath.Join(sourceDir, "fonts", "Roboto")
	if _, err := os.Stat(fontDir); err == nil {
		return fontDir
	}
	
	// 如果上述方法都失败，返回默认的相对路径
	return "fonts/Roboto"
}

// FontLoader 字体加载器
type FontLoader struct {
	fontPaths     []string
	excludedFonts map[string]bool
	maxRetries    int
	fontCache     map[string][]byte // 字体文件缓存
}

// NewFontLoader 创建字体加载器实例
func NewFontLoader() *FontLoader {
	return &FontLoader{
		maxRetries:    3,
		excludedFonts: make(map[string]bool),
		fontCache:     make(map[string][]byte),
	}
}

// SetExcludedFonts 设置需要排除的字体文件
func (fl *FontLoader) SetExcludedFonts(fontPaths []string) {
	for _, path := range fontPaths {
		// 标准化路径以便匹配
		standardPath, _ := filepath.Abs(path)
		fl.excludedFonts[standardPath] = true
	}
}

// LoadFontIfNeeded 加载字体（如果尚未加载）
func (fl *FontLoader) LoadFontIfNeeded(fontDir string) error {
	// 检查是否已经加载了字体
	if len(fl.fontPaths) > 0 {
		return nil
	}
	
	// 验证字体目录是否存在
	if _, err := os.Stat(fontDir); os.IsNotExist(err) {
		// 如果当前字体目录不存在，尝试使用默认字体目录
		defaultFontDir := GetDefaultFontDir()
		if fontDir != defaultFontDir {
			fontDir = defaultFontDir
			if _, err := os.Stat(fontDir); os.IsNotExist(err) {
				return fmt.Errorf("字体目录不存在: %s", fontDir)
			}
		} else {
			return fmt.Errorf("字体目录不存在: %s", fontDir)
		}
	}
	
	// 加载字体文件
	dirs := []string{fontDir}
	if err := fl.LoadFontsFromDirectories(dirs); err != nil {
		return fmt.Errorf("验证码加载失败: %w", err)
	}
	
	// 检查是否有可用的字体
	if len(fl.fontPaths) == 0 {
		return fmt.Errorf("没有找到可用的字体文件，请确保在字体目录中放置有效的.ttf或.otf格式字体文件")
	}
	
	return nil
}

// LoadFontsFromDirectories 从指定的目录加载字体文件
func (fl *FontLoader) LoadFontsFromDirectories(dirs []string) error {
	fontTypes := []string{"ttf", "otf"}
	fl.fontPaths = []string{}

	for _, dir := range dirs {
		entries, err := os.ReadDir(dir)
		if err != nil {
			// 如果直接读取失败，尝试使用相对于当前工作目录的路径
			currentDir, _ := os.Getwd()
			relativeDir := filepath.Join(currentDir, dir)
			entries, err = os.ReadDir(relativeDir)
			if err != nil {
				continue
			}
			dir = relativeDir
		}

		for _, entry := range entries {
			if !entry.IsDir() {
				ext := strings.ToLower(filepath.Ext(entry.Name()))
				ext = strings.TrimLeft(ext, ".")

				for _, fontType := range fontTypes {
					if ext == fontType {
						fontPath := filepath.Join(dir, entry.Name())
						fl.fontPaths = append(fl.fontPaths, fontPath)
						break
					}
				}
			}
		}
	}

	// 如果没有找到字体文件，返回错误
	if len(fl.fontPaths) == 0 {
		return errors.New("没有找到字体文件")
	}

	return nil
}

// GetRandomFontPath 获取随机字体文件路径
func (fl *FontLoader) GetRandomFontPath() (string, error) {
	if len(fl.fontPaths) == 0 {
		return "", errors.New("没有可用的字体文件")
	}

	// 尝试最多10次查找未被排除的字体
	for attempt := 0; attempt < 10; attempt++ {
		// 使用crypto/rand生成更安全的随机索引
		index, _ := rand.Int(rand.Reader, big.NewInt(int64(len(fl.fontPaths))))
		fontPath := fl.fontPaths[index.Int64()]
		
		// 标准化路径以便匹配
		standardPath, _ := filepath.Abs(fontPath)
		
		// 检查是否在排除列表中
		if !fl.excludedFonts[standardPath] {
			return fontPath, nil
		}
	}
	
	// 如果10次尝试后都找不到未被排除的字体，返回任意一个字体
	index, _ := rand.Int(rand.Reader, big.NewInt(int64(len(fl.fontPaths))))
	return fl.fontPaths[index.Int64()], nil
}

// TryLoadRandomFont 尝试加载随机字体文件，失败时重试
func (fl *FontLoader) TryLoadRandomFont() ([]byte, string, error) {
	// 尝试多次加载随机字体
	for i := 0; i < fl.maxRetries; i++ {
		fontPath, err := fl.GetRandomFontPath()
		if err != nil {
			return nil, "", err
		}

		// 标准化路径以便匹配缓存
		standardPath, _ := filepath.Abs(fontPath)

		// 先检查缓存中是否有字体
		if fontData, ok := fl.fontCache[standardPath]; ok {
			return fontData, fontPath, nil
		}

		// 尝试读取字体文件
		fontData, err := os.ReadFile(fontPath)
		if err != nil {
			// 打印出失败的字体文件路径
			fmt.Printf("加载字体文件失败: %s, 错误: %v\n", fontPath, err)
			continue
		}

		// 将字体文件内容存入缓存
		fl.fontCache[standardPath] = fontData

		return fontData, fontPath, nil
	}

	// 所有字体加载尝试都失败了
	return nil, "", fmt.Errorf("所有字体加载尝试都失败了")
}
